package Cliente.Controller;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

import Cliente.GUI.VentanaCliente;
import Dominio.Medida;
import Dominio.Sensor;
import Dominio.Usuario;
import Utils.MensajeUtils;
import Utils.SocketManager;

/**
 * <p>
 * Clase encargada de gestionar la conexion con el servidor en la parte cliente del programa.
 * </p>
 *
 */
public class ControllerCliente {

	final static int PORT = 5656;
	final static String CLRF = "\n";

	private SocketManager socket;
	private VentanaCliente cliente;
	private String ip;
	private Usuario usuario;
	
	public ControllerCliente() {
		cliente = new VentanaCliente(this);
	}

	public void conectar(String ip) {
		try {
			socket = new SocketManager(ip, PORT);
			String[] mensaje = MensajeUtils.descomponer(socket.Leer());
			if (mensaje[0].equals("501")) {
				this.ip = ip;
				cliente.siguientPanel();
			} else {
				cliente.mostrarError(mensaje[1]);
			}
		} catch (IOException e) {
			cliente.mostrarError("No se puede alcanzar el servidor.");
		}
	}

	public void login(String username, String pass) {
		try {
			if (comprobarConexion()) {
				socket.Escribir("USER " + username + CLRF);
				String[] respuesta = MensajeUtils.descomponer(socket.Leer());
				if (!respuesta[0].equals("201")) {
					cliente.mostrarError(respuesta[1]);
				} else {
					socket.Escribir("PASS " + pass + CLRF);
					respuesta = MensajeUtils.descomponer(socket.Leer());
					if (!respuesta[0].equals("202")) {
						cliente.mostrarError(respuesta[1]);
					} else {
						usuario = new Usuario(username, pass);
						cliente.siguientPanel();
					}
				}
			}
		} catch (IOException e) {
			cerrarConexion();
		}

	}

	public void listSensor() {
		try {
			if (comprobarConexion()) {
				socket.Escribir("LISTSENSOR" + CLRF);
				String[] respuesta = MensajeUtils.descomponer(socket.Leer());
				if (!respuesta[0].equals("112")) {
					cliente.mostrarError(respuesta[1]);
				} else {
					List<Sensor> lista = new ArrayList<Sensor>();
					String s = socket.Leer();
					String cod = MensajeUtils.descomponer(s)[0];
					while (!cod.equals("212")) {
						lista.add(MensajeUtils.descomponerSensor(s));
						s = socket.Leer();
						cod = MensajeUtils.descomponer(s)[0];
					}
					cliente.actualizarLista(lista);
				}
			}
		} catch (IOException e) {
			cerrarConexion();
		}
	}

	public void historico(int id) {
		try {
			if (comprobarConexion()) {
				socket.Escribir("HISTORICO " + id + CLRF);
				String[] respuesta = MensajeUtils.descomponer(socket.Leer());
				if (!respuesta[0].equals("113")) {
					cliente.mostrarError(respuesta[1]);
				} else {
					List<Medida> lista = new ArrayList<Medida>();
					String s = socket.Leer();
					String cod = MensajeUtils.descomponer(s)[0];
					while (!cod.equals("212")) {
						lista.add(MensajeUtils.descomponerMedida(s));
						s = socket.Leer();
						cod = MensajeUtils.descomponer(s)[0];
					}
					cliente.actualizarTabla(lista);
				}
			}
		} catch (IOException | NumberFormatException | ParseException e) {
			cerrarConexion();
		}
	}

	public void on(int id) {
		try {
			if (comprobarConexion()) {
				socket.Escribir("ON " + id + CLRF);
				String[] respuesta = MensajeUtils.descomponer(socket.Leer());
				if (!respuesta[0].equals("203")) {
					cliente.mostrarError(respuesta[1]);
				} else {
					cliente.mostrarMensaje(respuesta[1]);
				}
			}
		} catch (IOException e) {
			cerrarConexion();
		}
	}

	public void off(int id) {
		try {
			if (comprobarConexion()) {
				socket.Escribir("OFF " + id + CLRF);
				String[] respuesta = MensajeUtils.descomponer(socket.Leer());
				if (!respuesta[0].equals("204")) {
					cliente.mostrarError(respuesta[1]);
				} else {
					cliente.mostrarMensaje(respuesta[1]);
				}
			}
		} catch (IOException e) {
			cerrarConexion();
		}
	}

	public void ongps() {
		try {
			if (comprobarConexion()) {
				socket.Escribir("ONGPS" + CLRF);
				String[] respuesta = MensajeUtils.descomponer(socket.Leer());
				if (!respuesta[0].equals("205")) {
					cliente.mostrarError(respuesta[1]);
				} else {
					cliente.mostrarMensaje(respuesta[1]);
				}
			}
		} catch (IOException e) {
			cerrarConexion();
		}
	}

	public void offgps() {
		try {
			if (comprobarConexion()) {
				socket.Escribir("OFFGPS " + CLRF);
				String[] respuesta = MensajeUtils.descomponer(socket.Leer());
				if (!respuesta[0].equals("206")) {
					cliente.mostrarError(respuesta[1]);
				} else {
					cliente.mostrarMensaje(respuesta[1]);
				}
			}
		} catch (IOException e) {
			cerrarConexion();

		}
	}

	public void getvalact(int id) {
		try {
			if (comprobarConexion()) {
				socket.Escribir("GET_VALACT " + id + CLRF);
				String[] respuesta = MensajeUtils.descomponer(socket.Leer());
				if (!respuesta[0].equals("114")) {
					cliente.mostrarError(respuesta[1]);
				} else {
					cliente.actualizarMedida(MensajeUtils.descomponerMedida(MensajeUtils.descomponer(respuesta[1])[1]));
				}
			}
		} catch (IOException | NumberFormatException | ParseException e) {
			cerrarConexion();
		}
	}

	/**
	 * <p>
	 * Metodo para obtener la foto. 
	 * NOTA: Datas las aparentes corrupciones aleatorias en algunos datos que han aparecido en
	 * los programas de prueba, se reinicia la conexion cada vez que hay un fallo, hasta obtener una foto correcta, lo que
	 * puede suponer un bloqueo de algunos pocos segundos.
	 * </p>
	 */
	public void getfoto() {
		try {
			if (comprobarConexion()) {
				boolean correcto = true;
				socket.Escribir("GET_FOTO " + CLRF);
				String[] respuesta = MensajeUtils.descomponer(socket.Leer());
				if (respuesta[0].equals("206")) {
					byte[] buffer = socket.LeerBytes();
					while(buffer == null && correcto){
						socket.CerrarStreams();
						socket.CerrarSocket();
						socket = new SocketManager(ip, PORT);
						socket.Leer();
						socket.Escribir("USER " + usuario.getNombre() + CLRF);
						socket.Leer();
						socket.Escribir("PASS " + usuario.getClave() + CLRF);
						socket.Leer();
						socket.Escribir("GET_FOTO " + CLRF);
						respuesta = MensajeUtils.descomponer(socket.Leer());
						if(!respuesta[0].equals("206")){
							correcto = false;
						}
						buffer = socket.LeerBytes();
					}
					if(correcto){
						cliente.actualizarFoto(buffer);
						socket.Escribir("GET_LOC" + CLRF);
						String [] localizacion = MensajeUtils.descomponer(socket.Leer());
						if(localizacion[0].equals("114")){
							cliente.actualizarCoordenadas(MensajeUtils.descomponerLocalizacion(MensajeUtils.descomponer(localizacion[1])[1]));
						} else {
							cliente.mostrarError("No se han podido obtener las coordenadas");
						}
					} else {
						cliente.limpiarFoto();
						cliente.mostrarError("No se ha podido obtener la foto.");
					}
				} else {
					cliente.limpiarFoto();
					cliente.mostrarError(respuesta[1]);
				}
			}
		} catch (IOException e) {
			cerrarConexion();

		}
	}

	private boolean comprobarConexion() throws IOException {
		if (socket.tieneDatos()) {
			if (MensajeUtils.descomponer(socket.Leer())[0].equals("208")) {
				cerrarConexion("Has sido expulsado del servidor");
				return false;
			} else {
				return true;
			}
		}
		return true;
	}

	public void cerrarConexion() {
		try {
			if (comprobarConexion()) {
				ip = "";
				usuario = null;
				socket.Escribir("SALIR" + CLRF);
				socket.Leer();
				socket.CerrarStreams();
				socket.CerrarSocket();
				cliente.volverAInicio();
				cliente.mostrarError("No se puede alcanzar el servidor");
			}
		} catch (IOException e) {
			cliente.volverAInicio();
			cliente.mostrarError("No se puede alcanzar el servidor");
		}
	}
	
	public void cerrarConexion(String s) {
		try {
			if (comprobarConexion()) {
				ip = "";
				usuario = null;
				socket.Escribir("SALIR" + CLRF);
				socket.Leer();
				socket.CerrarStreams();
				socket.CerrarSocket();
				cliente.volverAInicio();
				cliente.mostrarError(s);
			}
		} catch (IOException e) {
			cliente.volverAInicio();
			cliente.mostrarError(s);
		}
	}

}
